home *** CD-ROM | disk | FTP | other *** search
- //*****************************************************************************
- //
- // FILE: DBExtern.cpp
- //
- // WRITTEN BY: Keimpe
- //
- // DATE: 1/94
- //
- // UPDATED: 5/94
- //
- // REVISION: $Revision: 2.10 $
- //
- // VERSION: Visual dBASE
- //
- // DESCRIPTION:
- // C++ part of the example that shows how the EXTERN system
- // can be used.
- //
- // For a description of this example, see the dbextern.prg file.
- //
- //*****************************************************************************
-
- // Include some headers.
- #include "dbasevar.h"
-
- //============================================================================
- //
- // You can create an instance of class CSession in DBaseInitInstance()
- // and set it up. In subsequent call backs, a pointer to this instance
- // can be retrieved with a call to GetSession(). Data that is kept on
- // a per-instance basis (DBaseVars for example) should be kept in the
- // session object.
- //
- //============================================================================
-
- class CSession {
- public:
- DVar ThisSessionLocalObject;
-
- CSession(){
- // Initialize objects local to this session.
-
- time_t t = time( NULL );
- ThisSessionLocalObject->SetZString( ctime( &t ) );
- }
- ~CSession(){
- // Destroy objects local to this session.
- }
-
- char * UseLocalObject(){
- // Use objects local to this session.
- return ThisSessionLocalObject->String();
- }
- };
-
-
- extern "C" {
- //============================================================================
- //
- // Usual DLL functions.
- //
- //============================================================================
-
- int FAR PASCAL LibMain(HINSTANCE, WORD, WORD, LPSTR){
-
- return 1;
- }
- int CALLBACK WEP(int /*nParam*/){return(1);}
-
- //
- // This function is called every time an Instance of Visual dBASE loads the DLL.
- // If for some reason the DLL determines that it cannot load, it can return
- // an error. If the DLL loads properly it should return DBASE_INIT_OK.
- //
-
- int CALLBACK DBaseInitInstance(void){
-
- asm int 3; // break point for the debugger.
-
- DBase()->SetSession(new CSession());
-
- //
- // Possible Error return Values
- //
- //return DBASE_INIT_NO_MULTI_INSTANCE;
- //return DBASE_INIT_ERROR;
-
- return DBASE_INIT_OK;
- }
-
- //
- // DBaseExitInstance() is called when an instance of dBASE terminates or
- // manually unloads a DLL through the RELEASE DLL command. This give the
- // DLL a chance to free any resources associated with a running instance.
- //
-
- void CALLBACK DBaseExitInstance(void){
- }
-
- //============================================================================
- //
- // Example # 1.
- //
- // Some simple examples using the different types.
- //
- //============================================================================
-
- BOOL _export _pascal TypeTest1( char * Str, int Int, DoubleType Double ) {
- if( strcmp( Str, "string" ) != 0 || Int != 123 || Double != 321.123L )
- return 0;
- return 1;
- }
-
- DoubleType _export _cdecl TypeTest2( char * Str, long Long, DBaseVar *Var ) {
- return strtod( Str, NULL ) + (DoubleType) Long + Var->Double();
- }
-
- //============================================================================
- //
- // Example # 3.
- //
- // Visual dBASE objects can be of various types. With the Type() function you
- // can recognize what kind of object you're dealing with.
- //
- //============================================================================
-
- char* _export _pascal WhichType( DBaseVar *var ) {
-
- static char buffer[25];
-
- // Get the type and put it in buffer.
- switch( var->Type() ){
- case 'L': // Logical
- strcpy( buffer, "Logical " );
- strcat( buffer, var->Logical() ? ".T." : ".F.");
- break;
- case 'N': // Numeric double
- strcpy( buffer, "Double " );
- gcvt(var->Double(),10,buffer+7);
- break;
- case 'I': // Numeric long
- strcpy( buffer, "Long " );
- ltoa(var->Long(), buffer+5, 10);
- break;
- case 'C': // String
- strcpy( buffer, "String " );
- strcat( buffer, var->String() );
- break;
- case 'O': // Object
- strcpy( buffer, "Object" );
- break;
- case 'F': // Function or code block
- strcpy( buffer, "Code" );
- break;
- default: // Anybody else
- buffer[0] = var->Type();
- buffer[1] = ' ';
- strcpy( buffer+2, ":Unknown Type" );
- }
- // Return the type.
- return buffer;
- }
-
- //============================================================================
- //
- // Example # 4.
- //
- // You can modify Visual dBASE objects passed in. Since they are passed by
- // reference, changes made here to objects show also on the Visual dBASE side.
- // It is also possible to use the CVAR type as the return type of Visual dBASE
- // functions. On the Visual dBASE side you simply declare the function
- // with CVAR as the return type:
- //
- // EXTERN CDECL CVAR ModifyAndAdd1( CVAR, CVAR ) dbextern.dll
- // EXTERN CVAR ModifyAndAdd2( CVAR, CVAR ) dbextern.dll
- //
- // but on the C/C++ side you use void as the return type of the function
- // and list the CVAR return variable as the first parameter for CDECL
- // functions or as the last parameter for PASCAL functions.
- //
- //============================================================================
-
- void _export _cdecl ModifyAndAdd1( DBaseVar *ReturnValue,
- DBaseVar *D1, DBaseVar *D2 ) {
-
- ReturnValue->SetDouble( D1->Double() + (DoubleType)D2->Long() );
- D1->SetZString( "Changed to a string" );
- D2->SetLogical( 1 );
- }
-
- void _export _pascal ModifyAndAdd2( DBaseVar *D1, DBaseVar *D2,
- DBaseVar *ReturnValue ) {
-
- ReturnValue->SetDouble( D1->Double() + (DoubleType)D2->Long() );
- D1->SetZString( "Changed in ModifyAndAdd2" );
- D2->SetLogical( 1 );
- }
-
- //============================================================================
- //
- // Example # 5.
- //
- // Parts of Visual dBASE class objects are accessible on this side.
- // This example shows one way of how to implement Windows API calls
- // that require the use of structs.
- //
- //============================================================================
-
- void _export _cdecl WSetRect( DBaseVar *rect, WORD left, WORD top,
- WORD right, WORD bottom ){
- RECT wrect;
- DVar Temp;
-
- // Call the Windows API.
- SetRect( &wrect, left, top, right, bottom );
-
- // Fill in the DBaseVar.
- rect->SetProperty( "left", DVar( (long)(wrect.left) ) );
- rect->SetProperty( "top", DVar( (long)(wrect.top) ) );
- rect->SetProperty( "right", DVar( (long)(wrect.right) ) );
- rect->SetProperty( "bottom", DVar( (long)(wrect.bottom) ) );
- }
-
- void _export _pascal WIntersectRect( DBaseVar *result,
- DBaseVar *rect1, DBaseVar *rect2 ){
- RECT one, two, three;
- DVar Temp;
-
- // Copy rect1 to one.
- rect1->Property( "left", Temp );
- one.left = Temp->Long();
- rect1->Property( "top", Temp );
- one.top = Temp->Long();
- rect1->Property( "right", Temp );
- one.right = Temp->Long();
- rect1->Property( "bottom", Temp );
- one.bottom = Temp->Long();
-
- // Copy rect2 to two.
- rect2->Property( "left", Temp );
- two.left = Temp->Long();
- rect2->Property( "top", Temp );
- two.top = Temp->Long();
- rect2->Property( "right", Temp );
- two.right = Temp->Long();
- rect2->Property( "bottom", Temp );
- two.bottom = Temp->Long();
-
- // Make the Windows API call
- IntersectRect( &three, &one, &two );
-
- // Copy three to result.
- Temp->SetLong( three.left );
- result->SetProperty( "left", Temp );
- Temp->SetLong( three.top );
- result->SetProperty( "top", Temp );
- Temp->SetLong( three.right );
- result->SetProperty( "right", Temp );
- Temp->SetLong( three.bottom );
- result->SetProperty( "bottom", Temp );
- }
-
- //============================================================================
- //
- // Example # 6.
- //
- // Through the use of code blocks Visual dBASE code can be executed on the fly
- // inside the DLL. The 2 objects that are passed in are compared, changed,
- // and compared again, all through codeblocks.
- //
- //============================================================================
-
- BOOL _cdecl AreEqual( DBaseVar *one, DBaseVar * two ){
-
- DVar Temp, Result;
-
- // Check that the types are equal.
- if( one->Type() != two->Type() )
- return 0;
-
- // Compare them and return the result.
- Temp->SetCodeBlock( "{|a,b| a=b}" );
- Temp->RunCodeBlock( Result,2,&one );
- return Result->Logical();
- }
-
- char * _export _pascal CheckAndChange( DBaseVar *one, DBaseVar *two ) {
-
- BOOL Result1, Result2;
- DVar Temp1, Temp2, Temp3( 3.0L );
-
- // Temp3Ptr is used to pass a DVar to a function that
- // expects a DBaseVar**.
- DBaseVar * Temp3Ptr = Temp3;
-
- // Announce that we arrived.
- Temp1->SetCodeBlock( "{ ;? 'Inside CheckAndChange' }" );
- Temp1->RunCodeBlock( Temp2, 0, (DVar*)0 );
-
- // Check if they are equal coming in.
- Result1 = AreEqual( one, two );
-
- // Change the 2 objects through codeblocks.
- Temp1->SetCodeBlock( "{|| 4.0 }" );
- Temp1->RunCodeBlock( one, 0, (DVar*)0 );
- Temp1->SetCodeBlock( "{ |a| a }" );
- Temp1->RunCodeBlock( two, 1, &Temp3Ptr );
-
- // Check that they are not equal anymore.
- Result2 = AreEqual( one, two );
-
- // Return the result.
- if( Result1 == 1 && Result2 == 0 )
- return "passed"; else return "failed";
- }
-
- //============================================================================
- //
- // Example # 7.
- //
- // Visual dBASE arrays can be accessed in the DLL. This example computes the
- // average of the members of an array created on the Visual dBASE side.
- // The DLL function is called without any parameters. To be able to
- // get at the array, the DLL function is executed through a function
- // pointer that is a member of the same object as the array. Inside
- // the DLL you can get the "this" pointer of the object the caller
- // belongs to, and through that "this" pointer you can access the
- // array members.
- // Normally ofcourse you can just pass the array into a DBaseVar.
- //
- //============================================================================
-
- DoubleType _export _cdecl ComputeAverage(){
-
- DoubleType d = 0;
- long iSize;
- int iCount=0;
- int i = 1;
- DVar pValue;
- DVar Size;
-
- // pSize is used to pass Size to functions expecting a DBaseVar**.
- DBaseVar *pSize = Size;
-
- // Initialize pThis with the "this" of the object the caller
- // belongs to.
- DVar pThis(DBase()->GetThis());
-
- // If we're not inside the context of a memberfunction return.
- if(pThis == 0) return 0;
-
- // Get the Size of the array of the object.
- pThis->Property("SIZE",Size);
- iSize = Size->Long();
-
- // Loop thru the array and total the elements.
- while( i < iSize){
- Size->SetLong(i);
- pThis->Element(pValue,1,&pSize);
- DoubleType d1;
- d += pValue->Double();
- iCount++;
- i++;
- }
- // Return the average.
- return d/iCount;
- }
-
- //============================================================================
- //
- // Example # 8.
- //
- // This example puts into "practice" a majority of the available
- // memberfunctions of the DBaseVar class.
- //
- //============================================================================
-
- long _pascal _export TestABunch( DBaseVar *Var ){
-
- DVar Elems1[6], Elems2[6];
- DVar Temp;
- long Errors = 0;
-
- // Set up 6 different objects. Check returns of Set.. functions.
- if( Elems1[0]->SetLogical( 1 ) != 1 )
- Errors += 1;
- if( Elems1[1]->SetDouble( 2.0L ) != 2.0L )
- Errors += 2;
- if( Elems1[2]->SetLong( 3L ) != 3L )
- Errors += 4;
- if( strcmp( Elems1[3]->SetZString( "Hi" ), "Hi" ) != 0 )
- Errors += 8;
- if( Elems1[4]->SetLong( 100L ) != 100L )
- Errors += 16;
- if( Elems1[5]->SetDouble( 5.5L ) != 5.5L )
- Errors += 32;
-
- // pParams is used to be able to pass params in one block.
- DBaseVar *pParams[2];
- DVar Param1;
- DVar Param2;
-
- // Set up the array of params.
- pParams[0] = Param1;
- pParams[1] = Param2;
-
- // Make a 2 by 3 array.
- Temp->SetCodeBlock("{|a,b| new array(a,b)}");
- Param1->SetLong(2);
- Param2->SetLong(3);
- Temp->RunCodeBlock(Var,2,pParams);
-
- // Fill in the array.
- for( int i=0; i<6; i++ ){
- Param1->SetLong( 1 + i % 2 ); // 1 - 2
- Param2->SetLong( 1 + i % 3 ); // 1 - 3
- Var->SetElement( 2,pParams,Elems1[i] );
- }
-
- // Read back the array into a different variable.
- for( i=0; i<6; i++ ){
- Param1->SetLong( 1 + i % 2 ); // 1 - 2
- Param2->SetLong( 1 + i % 3 ); // 1 - 3
- Var->Element( Elems2[i],2,pParams );
- }
-
- // Now check if it worked.
- Temp->Set( Elems2[0] );
- if( Temp->Logical() != 1 || Temp->Type() != 'L' )
- Errors += 64;
- if( !AreEqual( Elems1[0], Elems2[0] ) )
- Errors += 64;
- Temp->Set( Elems2[1] );
- if( Temp->Double() != 2.0L || Temp->Type() != 'N' )
- Errors += 128;
- if( !AreEqual( Elems1[1], Elems2[1] ) )
- Errors += 128;
- Temp->Set( Elems2[2] );
- if( Temp->Long() != 3L || Temp->Type() != 'I' )
- Errors += 256;
- if( !AreEqual( Elems1[2], Elems2[2] ) )
- Errors += 256;
- Temp->Set( Elems2[3] );
- if( strcmp( Temp->String(), "Hi" ) != 0 || Temp->Type() != 'C' )
- Errors += 512;
- if( !AreEqual( Elems1[3], Elems2[3] ) )
- Errors += 512;
- Temp->Set( Elems2[4] );
- if( Temp->Long() != 100L || Temp->Type() != 'I' )
- Errors += 1024;
- if( !AreEqual( Elems1[4], Elems2[4] ) )
- Errors += 1024;
- Temp->Set( Elems2[5] );
- if( Temp->Double() != 5.5L || Temp->Type() != 'N' )
- Errors += 2048;
- if( !AreEqual( Elems1[5], Elems2[5] ) )
- Errors += 2048;
-
- // And check Var using properties.
- Var->Property( "SIZE", Temp );
- if( Temp->Long() != 6 )
- Errors += 4096;
- Var->Property( "DIMENSIONS", Temp );
- if( Temp->Long() != 2 )
- Errors += 4096;
-
- return Errors;
- }
-
- //============================================================================
- //
- // Example # 9.
- //
- // This example grabs the DOS environment, puts it in a string and sends
- // it over to the Visual dBASE side.
- //
- //============================================================================
-
- void _export _cdecl GetEnvString(DBaseVar *pVarReturn){
-
- char *pEnviron = *environ;
-
- // Find the length and set it.
- while(strlen(pEnviron) != 0){
- pEnviron += strlen(pEnviron)+1;
- }
- int environLen = pEnviron - *environ;
-
- // Make room for the string and copy it.
- pVarReturn->SetStringLen(environLen);
- memcpy(pVarReturn->StringBuffer(),*environ,environLen);
-
- // Go through the string and change the zero characters into blanks.
- int count = 0;
- pEnviron = pVarReturn->StringBuffer();
-
- while( count < (environLen - 1) ) {
- if( *( pEnviron + count ) == 0x0 )
- *( pEnviron + count ) = ' ';
- count++;
- }
- }
-
- //============================================================================
- //
- // Example # 10.
- //
- // This example is called with a variable number of parameters using
- // the func(...) syntax.
- //
- //============================================================================
-
- DoubleType _cdecl _export AddNumbers( int pCount, DBaseVar *pFirstNum){
-
- // pFirstNum points to the parameters.
- DBaseVar **ppVars = &pFirstNum;
- DoubleType d = 0;
-
- // Go thru the parameters and add them.
- while(pCount--){
- d += (*ppVars)->Double();
- ppVars++;
- }
- return d;
- }
-
- //============================================================================
- //
- // Example 11.
- //
- // This example shows how a Visual dBASE object can be combined with an "hidden"
- // C++ sister object. Each Visual dBASE object has its own unique C++ object.
- // This allows a Visual dBASE object to "remember and store" its own details
- // specific to the DLL in the C++ object instead of having to save it
- // in the Visual dBASE object. Examples are the storing of filehandles, and
- // filepointers when the DLL is servicing files.
- // Another possibility is a form of late binding where at the time of
- // creation of the C++ part a choice can be made from several derived
- // classes based on a common parent object.
- //
- //============================================================================
-
- //
- // Common parent object
- //
- class DLLObject {
- protected:
- char Message[60];
- char ID[30];
- public:
- DLLObject() {}
- virtual char * Doit() = 0;
- };
-
- //
- // ObjectA
- //
- class ObjectA : public DLLObject {
- public:
- ObjectA( char *In ) { strcpy( ID, In ); }
- char * Doit();
- };
-
- char * ObjectA::Doit() {
- strcpy( Message, ID );
- strcat( Message, "you can read this" );
- return Message;
- }
-
- //
- // ObjectB
- //
- class ObjectB : public DLLObject {
- public:
- ObjectB( char * In ) { strcpy( ID, In ); }
- char * Doit();
- };
-
- char * ObjectB::Doit() {
- strcpy( Message, ID );
- strcat( Message, "siht daer t'nac uoy" );
- return Message;
- }
-
- //
- // Helper function to retrieve the C++ this pointer from
- // the calling Visual dBASE object.
- //
- DLLObject *GetCPlusPlusThis() {
-
- // Local vars. Get the this of the Visual dBASE object.
- DVar CThis, DBaseThis( DBase()->GetThis() );
-
- // Get the this of the corresponding C++ object.
- DBaseThis->Property( "cthis", CThis );
-
- return (DLLObject*)(CThis->Long());
- }
-
- //
- // Initializes the C++ object. Inserts the C++ this pointer into
- // the calling Visual dBASE object.
- //
- int _export _pascal DLLObjectInit( int Number ) {
-
- DLLObject * ThisDLLObject;
- char Message[50];
- static int Count = 0;
-
- // Get the this of the calling Visual dBASE object.
- DVar CThis( DBase()->GetThis() );
-
- // Assign unique id, store object type.
- sprintf( Message, "ID# %d, Type: Object #%d ", ++Count, Number );
-
- // Choose what kind of C++ object to create.
- if( Number == 1 ) {
-
- #ifdef NO_EXCEPTIONS
-
- // Create new C++ object.
- ThisDLLObject = new ObjectA( Message );
- if( ThisDLLObject == NULL )
- return 0;
-
- // Sneak this pointer into the Visual dBASE object.
- CThis->SetProperty( "cthis", DVar( (long) ThisDLLObject ) );
- }
- else {
- // Create new C++ object.
- ThisDLLObject = new ObjectB( Message );
- if( ThisDLLObject == NULL )
- return 0;
-
- // Sneak this pointer into the Visual dBASE object.
- CThis->SetProperty( "cthis", DVar( (long) ThisDLLObject ) );
-
- #else // NO_EXCEPTIONS
-
- try {
- // Create new C++ object.
- ThisDLLObject = new ObjectA( Message );
- // Sneak this pointer into the Visual dBASE object.
- CThis->SetProperty( "cthis", DVar( (long) ThisDLLObject ) );
- }
- catch( ... ) {
- return 0;
- }
- }
- else {
- try {
- // Create new C++ object.
- ThisDLLObject = new ObjectB( Message );
- // Sneak this pointer into the Visual dBASE object.
- CThis->SetProperty( "cthis", DVar( (long) ThisDLLObject ) );
- }
- catch( ... ) {
- return 0;
- }
-
- #endif // NO_EXCEPTIONS
-
- }
-
- return 1;
- }
-
- //
- // Helper function to call the Doit function tied to the
- // calling Visual dBASE object.
- //
- char * _export _pascal Doit() {
- return GetCPlusPlusThis()->Doit();
- }
-
- //
- // Helper function to delete the C++ object tied to the calling
- // Visual dBASE object.
- //
- void _export _pascal Release() {
- delete GetCPlusPlusThis();
- }
-
- // extern "C"
- }
-